fix cursor api prompt errors#838
Conversation
📝 WalkthroughWalkthroughAdds LF enforcement for shell scripts, normalizes docker-entrypoint.sh line endings in Docker builds, implements a WSL-aware temp-file fallback for long prompts, changes the CLI separator, ensures Cursor config/auth setup in entrypoint, adds IPv6 listen, and pins TypeScript devDependency. Changes
Sequence DiagramsequenceDiagram
participant Client
participant CursorProvider
participant FileSystem as FS
participant CLIProcess as CLI
Client->>CursorProvider: executeQuery(prompt, options)
CursorProvider->>CursorProvider: check prompt length
alt prompt > PROMPT_ARG_MAX_LENGTH
CursorProvider->>FS: write prompt -> temp file
FS-->>CursorProvider: temp file path
CursorProvider->>CursorProvider: build subprocess options (prompt-file, WSL/native)
CursorProvider->>CLI: spawn process (reads prompt from file)
CLI->>FS: read temp file
FS-->>CLI: prompt content
CLI-->>CursorProvider: result
CursorProvider->>FS: delete temp file
FS-->>CursorProvider: ack
else prompt within limit
CursorProvider->>CLI: spawn process (prompt as arg)
CLI-->>CursorProvider: result
end
CursorProvider-->>Client: return result
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses critical issues affecting the reliability of the Cursor agent, particularly concerning prompt handling and execution across different environments. The primary goal is to ensure that the Cursor model correctly receives and processes prompts, even when they are lengthy or contain special characters, by refining the command-line argument passing mechanism and introducing a robust temporary file fallback. Additionally, it includes several infrastructure improvements for Docker compatibility and dependency management. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request addresses an issue with the Cursor agent by switching from stdin to positional arguments for prompts and adding a robust temp-file fallback for long prompts, alongside improving Docker line ending handling. No specific security vulnerabilities were identified in this review. However, there are suggestions to refine file permissions in the docker-entrypoint.sh script and to use an asynchronous file operation in cursor-provider.ts for consistency and to avoid blocking the event loop.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/server/src/providers/cursor-provider.ts (1)
988-989: Consider restricting temp file permissions to owner-only.The prompt file is created with default permissions (typically 644), making it world-readable. Since prompts may contain sensitive instructions or context, consider setting explicit
0o600permissions:🔒 Proposed fix
fs.mkdirSync(automakerDir, { recursive: true }); - fs.writeFileSync(promptFilePath, promptText, 'utf8'); + fs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode: 0o600 });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 988 - 989, The prompt file is created with default world-readable permissions; update the creation to restrict access to the owner only by setting explicit modes: create the directory automakerDir with owner-only permissions (e.g., 0o700) via fs.mkdirSync(..., { recursive: true, mode: 0o700 }) and write the prompt file promptFilePath with owner-only file permissions (0o600) by using fs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode: 0o600 }) or, if you prefer, call fs.chmodSync(promptFilePath, 0o600) immediately after fs.writeFileSync; ensure you reference automakerDir, promptFilePath, and promptText when making the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 543-553: The NPX temp-file path is incorrectly wrapping the
command with npx and passing 'bash'/'-c' to the package; in the block where
detectedStrategy === 'npx' (see symbols: detectedStrategy, npxArgs, fullCommand)
change the returned process invocation to run bash directly instead of npx: set
command to 'bash' and args to ['-c', fullCommand] (preserving cwd, filteredEnv,
options.abortController, and timeout) so the full npx invocation in fullCommand
runs inside the shell rather than being passed as args to the cursor-agent
package.
---
Nitpick comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 988-989: The prompt file is created with default world-readable
permissions; update the creation to restrict access to the owner only by setting
explicit modes: create the directory automakerDir with owner-only permissions
(e.g., 0o700) via fs.mkdirSync(..., { recursive: true, mode: 0o700 }) and write
the prompt file promptFilePath with owner-only file permissions (0o600) by using
fs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode: 0o600 })
or, if you prefer, call fs.chmodSync(promptFilePath, 0o600) immediately after
fs.writeFileSync; ensure you reference automakerDir, promptFilePath, and
promptText when making the changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4152e5a2-a66e-4343-ab21-2df1ded9ec22
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (7)
.gitattributesDockerfileapps/server/src/providers/cli-provider.tsapps/server/src/providers/cursor-provider.tsapps/ui/nginx.confdocker-entrypoint.shpackage.json
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
apps/server/src/providers/cursor-provider.ts (1)
544-553:⚠️ Potential issue | 🟠 MajorNPX temp-file mode still wraps
bash -cthroughnpx.This branch builds
npx … bash -c …, sobashand-cbecome arguments to the Cursor package instead of running the shell command. Long prompts will still fail fordetectedStrategy === 'npx'.Suggested fix
if (this.detectedStrategy === 'npx') { - const allArgs = [...this.npxArgs, 'bash', '-c', fullCommand]; return { - command: 'npx', - args: allArgs, + command: 'bash', + args: ['-c', fullCommand], cwd, env: filteredEnv, abortController: options.abortController, timeout, }; }#!/bin/bash rg -n -C3 "detectedStrategy === 'npx'|command: 'npx'|bash', '-c'|fullCommand" apps/server/src/providers/cursor-provider.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 544 - 553, The npx branch currently constructs args [...this.npxArgs, 'bash', '-c', fullCommand] which makes 'bash' be treated as an npx package argument instead of a subprocess; change the returned args so bash is executed (e.g., insert the npx argument separator '--' before 'bash' so args become [...this.npxArgs, '--', 'bash', '-c', fullCommand]) or alternatively skip npx and set command to 'bash' with args ['-c', fullCommand]; update the code paths referencing detectedStrategy, npxArgs, fullCommand and the object returning command: 'npx' accordingly so long prompts are passed to the shell rather than to npx as package names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 983-1000: The temp prompt file is created and written before the
surrounding try/finally, so failures in subsequent setup (e.g.,
buildSubprocessOptionsWithPromptFile, buildCliArgs, getEffectivePermissions,
detectProfile) can leave promptFilePath on disk; move the temp-file creation and
write (getAutomakerDir, randomBytes, path.join, fs.mkdirSync, fs.writeFileSync)
inside the same try block that currently cleans up in finally (or expand the try
to start before creating the file) so that the finally always runs to delete
promptFilePath when useTempFile is true; keep existing calls to buildCliArgs,
buildSubprocessOptionsWithPromptFile, and subprocessOptions construction
unchanged but ensure they execute inside that try so cleanup is guaranteed.
- Around line 540-563: buildSubprocessOptionsWithPromptFile currently falls
through to "bash -c" for non-npx cases, which breaks native Windows ('cmd' or
'direct') installs; update the function to explicitly handle
this.detectedStrategy === 'cmd' || this.detectedStrategy === 'direct' (and not
using WSL) by returning a direct spawn instead of the bash wrapper: use
this.cliPath as the command and pass the prompt file reference/escaped args as
normal args (use the same escapedArgs/escapedPath variables used in the existing
code) and preserve cwd, filteredEnv, abortController/options.timeout; keep the
existing npx branch intact and only add this new branch before the final bash
fallback so Windows native installs run the binary directly.
---
Duplicate comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 544-553: The npx branch currently constructs args
[...this.npxArgs, 'bash', '-c', fullCommand] which makes 'bash' be treated as an
npx package argument instead of a subprocess; change the returned args so bash
is executed (e.g., insert the npx argument separator '--' before 'bash' so args
become [...this.npxArgs, '--', 'bash', '-c', fullCommand]) or alternatively skip
npx and set command to 'bash' with args ['-c', fullCommand]; update the code
paths referencing detectedStrategy, npxArgs, fullCommand and the object
returning command: 'npx' accordingly so long prompts are passed to the shell
rather than to npx as package names.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f2bebcd5-1ff6-4a68-be55-3a5fcbe574eb
📒 Files selected for processing (1)
apps/server/src/providers/cursor-provider.ts
| // Native (Linux/macOS) or npx/direct on Windows | ||
| const cliPath = this.cliPath; | ||
| const fullCommand = `${this.escapeForBashSingleQuoted(cliPath)} ${escapedArgs} "$(cat ${escapedPath})"`; | ||
|
|
||
| if (this.detectedStrategy === 'npx') { | ||
| const allArgs = [...this.npxArgs, 'bash', '-c', fullCommand]; | ||
| return { | ||
| command: 'npx', | ||
| args: allArgs, | ||
| cwd, | ||
| env: filteredEnv, | ||
| abortController: options.abortController, | ||
| timeout, | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| command: 'bash', | ||
| args: ['-c', fullCommand], | ||
| cwd, | ||
| env: filteredEnv, | ||
| abortController: options.abortController, | ||
| timeout, | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
printf '\nWindows detection branches:\n'
rg -n -C2 "useWsl: false|strategy: 'cmd'|strategy: 'direct'" apps/server/src/providers/cursor-provider.ts
printf '\nTemp-file subprocess branches:\n'
rg -n -C2 "buildSubprocessOptionsWithPromptFile|command: 'bash'|command: 'wsl.exe'" apps/server/src/providers/cursor-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 1606
🏁 Script executed:
sed -n '497,563p' apps/server/src/providers/cursor-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 2242
🏁 Script executed:
sed -n '400,500p' apps/server/src/providers/cursor-provider.ts | head -80Repository: AutoMaker-Org/automaker
Length of output: 2726
🏁 Script executed:
rg -n "buildSubprocessOptions[^W]|strategy.*cmd|strategy.*direct" apps/server/src/providers/cursor-provider.ts | head -20Repository: AutoMaker-Org/automaker
Length of output: 468
🏁 Script executed:
rg -n "private buildSubprocessOptions\(" apps/server/src/providers/cursor-provider.ts -A 100 | head -150Repository: AutoMaker-Org/automaker
Length of output: 49
🏁 Script executed:
rg -n "buildSubprocessOptions" apps/server/src/providers/cursor-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 348
🏁 Script executed:
rg -n "buildSubprocessOptions[^W]" apps/server/src/providers/cursor-provider.ts -B 2 -A 50Repository: AutoMaker-Org/automaker
Length of output: 3550
🏁 Script executed:
grep -n "buildSubprocessOptions" apps/server/src/providers/cursor-provider.ts | grep -v "buildSubprocessOptionsWithPromptFile"Repository: AutoMaker-Org/automaker
Length of output: 223
🏁 Script executed:
rg -n "^\s*(private|public|protected).*buildSubprocessOptions" apps/server/src/providers/cursor-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 118
🏁 Script executed:
rg -n "buildSubprocessOptions\s*\(" apps/server/src/providers/cursor-provider.ts -B 5 | grep -E "(private|public|protected|buildSubprocessOptions)" | head -20Repository: AutoMaker-Org/automaker
Length of output: 144
🏁 Script executed:
grep -n "class.*CursorProvider\|extends" apps/server/src/providers/cursor-provider.ts | head -5Repository: AutoMaker-Org/automaker
Length of output: 169
🏁 Script executed:
rg -l "class CliProvider" apps/server/src/Repository: AutoMaker-Org/automaker
Length of output: 108
🏁 Script executed:
rg -n "buildSubprocessOptions" apps/server/src/providers/ -lRepository: AutoMaker-Org/automaker
Length of output: 245
🏁 Script executed:
rg -n "buildSubprocessOptions" apps/server/src/providers/cli-provider.ts -A 50 | head -100Repository: AutoMaker-Org/automaker
Length of output: 3838
🏁 Script executed:
rg -n "buildSubprocessOptions" apps/server/src/providers/cli-provider.ts -A 80 | tail -50Repository: AutoMaker-Org/automaker
Length of output: 1883
🏁 Script executed:
sed -n '446,510p' apps/server/src/providers/cli-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 2209
🏁 Script executed:
sed -n '510,525p' apps/server/src/providers/cli-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 441
Add handler for native Windows (cmd/direct) strategy in buildSubprocessOptionsWithPromptFile to prevent bash command failure on long prompts.
The method buildSubprocessOptionsWithPromptFile (lines 540-563) only handles WSL and NPX strategies, then defaults to bash -c. However, detectCli() returns strategy: 'cmd' or strategy: 'direct' with useWsl: false for native Windows installations (cursor-agent.exe, .cmd, direct exe paths). When a prompt exceeds 24 KB and triggers this method, Windows native installs will attempt to run bash -c, which fails because those systems don't necessarily have bash available. The parent class buildSubprocessOptions correctly handles these strategies by returning a direct command spawn, but the temp-file variant doesn't.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/server/src/providers/cursor-provider.ts` around lines 540 - 563,
buildSubprocessOptionsWithPromptFile currently falls through to "bash -c" for
non-npx cases, which breaks native Windows ('cmd' or 'direct') installs; update
the function to explicitly handle this.detectedStrategy === 'cmd' ||
this.detectedStrategy === 'direct' (and not using WSL) by returning a direct
spawn instead of the bash wrapper: use this.cliPath as the command and pass the
prompt file reference/escaped args as normal args (use the same
escapedArgs/escapedPath variables used in the existing code) and preserve cwd,
filteredEnv, abortController/options.timeout; keep the existing npx branch
intact and only add this new branch before the final bash fallback so Windows
native installs run the binary directly.
| if (useTempFile) { | ||
| // Temp file fallback for long prompts (avoids platform command-line limits) | ||
| const cwd = options.cwd || process.cwd(); | ||
| const automakerDir = getAutomakerDir(cwd); | ||
| const promptId = randomBytes(8).toString('hex'); | ||
| promptFilePath = path.join(automakerDir, `.cursor-prompt-${promptId}`); | ||
| fs.mkdirSync(automakerDir, { recursive: true }); | ||
| fs.writeFileSync(promptFilePath, promptText, 'utf8'); | ||
| logger.debug( | ||
| `Prompt length ${promptText.length} exceeds limit; using temp file: ${promptFilePath}` | ||
| ); | ||
|
|
||
| // Pass prompt via stdin to avoid shell interpretation of special characters | ||
| // like $(), backticks, etc. that may appear in file content | ||
| subprocessOptions.stdinData = promptText; | ||
| cliArgs = this.buildCliArgs(effectiveOptions, { omitPrompt: true }); | ||
| subprocessOptions = this.buildSubprocessOptionsWithPromptFile( | ||
| options, | ||
| cliArgs, | ||
| promptFilePath | ||
| ); |
There was a problem hiding this comment.
Expand the try/finally to cover temp-file creation.
promptFilePath is written before the try starts. If anything throws during setup after that point—buildSubprocessOptionsWithPromptFile(), getEffectivePermissions(), or detectProfile()—the finally never runs and the full prompt is left on disk.
Also applies to: 1026-1128
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/server/src/providers/cursor-provider.ts` around lines 983 - 1000, The
temp prompt file is created and written before the surrounding try/finally, so
failures in subsequent setup (e.g., buildSubprocessOptionsWithPromptFile,
buildCliArgs, getEffectivePermissions, detectProfile) can leave promptFilePath
on disk; move the temp-file creation and write (getAutomakerDir, randomBytes,
path.join, fs.mkdirSync, fs.writeFileSync) inside the same try block that
currently cleans up in finally (or expand the try to start before creating the
file) so that the finally always runs to delete promptFilePath when useTempFile
is true; keep existing calls to buildCliArgs,
buildSubprocessOptionsWithPromptFile, and subprocessOptions construction
unchanged but ensure they execute inside that try so cleanup is guaranteed.
|
Please submit this agains the v1.0.0rc branch |
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
apps/server/src/providers/cursor-provider.ts (3)
544-553:⚠️ Potential issue | 🟠 MajorNPX temp-file execution path is still malformed.
This path invokes
npxwithbash -cas package args, so the shell command is not executed as intended for long prompts.🐛 Proposed fix
- if (this.detectedStrategy === 'npx') { - const allArgs = [...this.npxArgs, 'bash', '-c', fullCommand]; - return { - command: 'npx', - args: allArgs, - cwd, - env: filteredEnv, - abortController: options.abortController, - timeout, - }; - } + if (this.detectedStrategy === 'npx') { + const escapedNpxArgs = this.npxArgs.map((a) => this.escapeForBashSingleQuoted(a)).join(' '); + const npxCommand = `npx ${escapedNpxArgs} ${escapedArgs} "$(cat ${escapedPath})"`; + return { + command: 'bash', + args: ['-c', npxCommand], + cwd, + env: filteredEnv, + abortController: options.abortController, + timeout, + }; + }#!/bin/bash # Verify NPX branch construction in prompt-file mode rg -n -A20 -B4 "if \\(this\\.detectedStrategy === 'npx'\\)" apps/server/src/providers/cursor-provider.tsExpected: NPX branch should execute via a shell command (
command: 'bash'), not passbash -cthroughnpxargs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 544 - 553, The NPX branch currently returns command 'npx' with npxArgs plus 'bash' and '-c' in the args, which causes the shell to be passed as an NPX package instead of executing the shell command; update the block where this.detectedStrategy === 'npx' (and where npxArgs, fullCommand are used) to return command: 'bash' and args: ['-c', fullCommand] (preserve cwd, env: filteredEnv, abortController: options.abortController, and timeout) so the shell executes the temporary-file/fullCommand directly rather than being invoked through npx.
556-563:⚠️ Potential issue | 🟠 MajorLong-prompt fallback should not default to
bashfor native Windows strategies.For
cmd/directdetections on Windows, this branch can fail withbashnot found. Add an explicit strategy branch (or a clear fail-fast with guidance) before the bash fallback.💡 Minimal fail-fast safeguard
+ if ( + process.platform === 'win32' && + !this.useWsl && + (this.detectedStrategy === 'cmd' || this.detectedStrategy === 'direct') + ) { + throw new Error( + 'Long prompt execution on native Windows requires WSL. Install WSL or reduce prompt length.' + ); + } + return { command: 'bash', args: ['-c', fullCommand], cwd, env: filteredEnv,#!/bin/bash # Verify Windows strategy outputs and prompt-file fallback branches rg -n "strategy: 'cmd'|strategy: 'direct'" apps/server/src/providers/cursor-provider.ts rg -n -A35 -B6 "buildSubprocessOptionsWithPromptFile\\(" apps/server/src/providers/cursor-provider.tsExpected: prompt-file mode should explicitly handle native Windows strategies, not only WSL/NPX + bash fallback.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 556 - 563, The long-prompt fallback currently returns a bash-based subprocess spec which will fail on native Windows when strategy is 'cmd' or 'direct'; update buildSubprocessOptionsWithPromptFile (or the function that returns the object with command: 'bash', args: ['-c', fullCommand]) to first detect Windows-native strategies ('cmd' and 'direct') and either: (a) produce a Windows-compatible spec (use 'cmd.exe' with '/C' or the direct command invocation) for those strategies, or (b) immediately fail-fast with a clear error message instructing the user to enable WSL/NPX or switch strategy, so the code does not silently assume bash on Windows. Ensure you reference the same return object (command/args/env/abortController/timeout) when implementing the new branches.
983-1000:⚠️ Potential issue | 🟠 MajorTemp prompt cleanup is not guaranteed if setup throws before the
try.
promptFilePathis created and written before entering thetry. Any failure during subsequent setup can leave prompt data on disk.♻️ Proposed restructuring
- if (useTempFile) { - // create/write file... - ... - } else { - ... - } - - ... - try { + try { + if (useTempFile) { + // create/write file... + ... + } else { + ... + } + + ... for await (const rawEvent of spawnJSONLProcess(subprocessOptions)) { ... }Also applies to: 1026-1128
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 983 - 1000, The temp prompt file is created before the subsequent setup and thus can be leaked if an exception occurs; move the creation and write of promptFilePath into the same try block that later performs setup (or ensure a finally block always deletes promptFilePath), so that any throw after calling buildCliArgs or buildSubprocessOptionsWithPromptFile still triggers cleanup; specifically update the block that uses getAutomakerDir, randomBytes and fs.writeFileSync to either (A) create/write the file inside the try that wraps calls to this.buildCliArgs and this.buildSubprocessOptionsWithPromptFile, or (B) keep creation where it is but add a finally that checks promptFilePath and unlinks it, and apply the same change to the duplicate region later (around the code using buildCliArgs and buildSubprocessOptionsWithPromptFile).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 989-990: The code currently uses fs.mkdirSync and fs.writeFileSync
to create automakerDir and write prompt files (variables automakerDir,
promptFilePath, promptText) but these prompt files contain sensitive LLM content
and must be written with restricted permissions; import secureFs from
'@automaker/platform' (or alongside fs) and replace
fs.writeFileSync(promptFilePath, promptText, 'utf8') with
secureFs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode:
0o600 }) while still ensuring the directory exists (you may keep
fs.mkdirSync(automakerDir, { recursive: true }) or use secureFs if available for
dir creation) — follow the same pattern used in lib/auth.ts for API key files.
---
Duplicate comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 544-553: The NPX branch currently returns command 'npx' with
npxArgs plus 'bash' and '-c' in the args, which causes the shell to be passed as
an NPX package instead of executing the shell command; update the block where
this.detectedStrategy === 'npx' (and where npxArgs, fullCommand are used) to
return command: 'bash' and args: ['-c', fullCommand] (preserve cwd, env:
filteredEnv, abortController: options.abortController, and timeout) so the shell
executes the temporary-file/fullCommand directly rather than being invoked
through npx.
- Around line 556-563: The long-prompt fallback currently returns a bash-based
subprocess spec which will fail on native Windows when strategy is 'cmd' or
'direct'; update buildSubprocessOptionsWithPromptFile (or the function that
returns the object with command: 'bash', args: ['-c', fullCommand]) to first
detect Windows-native strategies ('cmd' and 'direct') and either: (a) produce a
Windows-compatible spec (use 'cmd.exe' with '/C' or the direct command
invocation) for those strategies, or (b) immediately fail-fast with a clear
error message instructing the user to enable WSL/NPX or switch strategy, so the
code does not silently assume bash on Windows. Ensure you reference the same
return object (command/args/env/abortController/timeout) when implementing the
new branches.
- Around line 983-1000: The temp prompt file is created before the subsequent
setup and thus can be leaked if an exception occurs; move the creation and write
of promptFilePath into the same try block that later performs setup (or ensure a
finally block always deletes promptFilePath), so that any throw after calling
buildCliArgs or buildSubprocessOptionsWithPromptFile still triggers cleanup;
specifically update the block that uses getAutomakerDir, randomBytes and
fs.writeFileSync to either (A) create/write the file inside the try that wraps
calls to this.buildCliArgs and this.buildSubprocessOptionsWithPromptFile, or (B)
keep creation where it is but add a finally that checks promptFilePath and
unlinks it, and apply the same change to the duplicate region later (around the
code using buildCliArgs and buildSubprocessOptionsWithPromptFile).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 72558e01-b383-4843-a261-602c8969f033
📒 Files selected for processing (2)
apps/server/src/providers/cursor-provider.tsdocker-entrypoint.sh
🚧 Files skipped from review as they are similar to previous changes (1)
- docker-entrypoint.sh
| fs.mkdirSync(automakerDir, { recursive: true }); | ||
| fs.writeFileSync(promptFilePath, promptText, 'utf8'); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify how temp prompt files are written
rg -n -A2 -B2 "writeFileSync\\(promptFilePath" apps/server/src/providers/cursor-provider.tsRepository: AutoMaker-Org/automaker
Length of output: 394
🏁 Script executed:
#!/bin/bash
# Find all writeFileSync calls in cursor-provider.ts to see if there are other cases
rg -n "writeFileSync" apps/server/src/providers/cursor-provider.ts
# Also check for other prompt/sensitive file writes in the server directory
rg -n "writeFileSync.*prompt" apps/server/src/Repository: AutoMaker-Org/automaker
Length of output: 239
🏁 Script executed:
#!/bin/bash
# Check for all writeFileSync calls in cursor-provider.ts to see if other files need protection
rg -n "writeFileSync" apps/server/src/providers/cursor-provider.ts -A 1 -B 1
# Check if there are other sensitive file writes (cache, config, state, etc.) in the file
rg -n "writeFileSync" apps/server/src/providers/cursor-provider.ts | wc -lRepository: AutoMaker-Org/automaker
Length of output: 215
🏁 Script executed:
#!/bin/bash
# Get more context around the writeFileSync call to understand the sensitivity
sed -n '970,1010p' apps/server/src/providers/cursor-provider.ts
# Also check for other writeFileSync calls in the broader server directory
rg -n "writeFileSync" apps/server/src/ | head -20Repository: AutoMaker-Org/automaker
Length of output: 2586
🏁 Script executed:
#!/bin/bash
# Check the secure-fs.ts implementation to understand the pattern
cat -n apps/server/src/lib/secure-fs.ts
# Check how auth.ts uses secureFs
grep -n -A 3 -B 3 "secureFs.writeFileSync" apps/server/src/lib/auth.tsRepository: AutoMaker-Org/automaker
Length of output: 1360
🏁 Script executed:
#!/bin/bash
# Check what fs module is imported in cursor-provider.ts
head -50 apps/server/src/providers/cursor-provider.ts | grep -n "import.*fs"
# Check if cursor-provider.ts should be using secureFs instead
grep -n "^import" apps/server/src/providers/cursor-provider.ts | head -20Repository: AutoMaker-Org/automaker
Length of output: 633
🏁 Script executed:
#!/bin/bash
# Check the context around the other writeFileSync calls to see if they also write sensitive data
# Start with gemini-usage-service.ts
sed -n '745,755p' apps/server/src/services/gemini-usage-service.ts
# Check cursor-config-manager.ts
sed -n '55,65p' apps/server/src/providers/cursor-config-manager.ts
# Check the deauth files
sed -n '20,30p' apps/server/src/routes/setup/routes/deauth-cursor.tsRepository: AutoMaker-Org/automaker
Length of output: 1153
Use secureFs for writing sensitive prompt files with restrictive permissions.
These temp prompt files contain sensitive LLM content and should use the established secureFs pattern (from @automaker/platform) with restricted file mode, matching how API keys are handled in lib/auth.ts. Replace the plain fs import and call with secureFs.writeFileSync() to ensure files are written with mode 0o600.
Proposed fix
- fs.writeFileSync(promptFilePath, promptText, 'utf8');
+ secureFs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode: 0o600 });Also update the import to use secureFs for this operation (or import it alongside fs).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/server/src/providers/cursor-provider.ts` around lines 989 - 990, The
code currently uses fs.mkdirSync and fs.writeFileSync to create automakerDir and
write prompt files (variables automakerDir, promptFilePath, promptText) but
these prompt files contain sensitive LLM content and must be written with
restricted permissions; import secureFs from '@automaker/platform' (or alongside
fs) and replace fs.writeFileSync(promptFilePath, promptText, 'utf8') with
secureFs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode:
0o600 }) while still ensuring the directory exists (you may keep
fs.mkdirSync(automakerDir, { recursive: true }) or use secureFs if available for
dir creation) — follow the same pattern used in lib/auth.ts for API key files.
There was a problem hiding this comment.
♻️ Duplicate comments (3)
apps/server/src/providers/cursor-provider.ts (3)
986-1007:⚠️ Potential issue | 🟠 MajorStart the
try/finallybefore creating the temp prompt file.If Lines 989-1006 or Lines 1018-1019 throw, the file created at Line 991 survives because the
finallyblock only starts at Line 1123. Wrap the temp-file creation and the rest of the pre-spawn setup in the sametry/finallyso cleanup is guaranteed.Also applies to: 1017-1021, 1123-1131
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 986 - 1007, The temp prompt file creation and all pre-spawn setup must be inside the same try/finally so the prompt file is always cleaned up; move the start of the try block to begin before the temp-file creation (the block that uses useTempFile, getAutomakerDir, randomBytes, promptFilePath, fs.writeFileSync) and keep the existing finally that removes promptFilePath, ensuring buildCliArgs, buildSubprocessOptionsWithPromptFile (and the else-path calling buildCliArgs/buildSubprocessOptions) are executed inside that try; update the scope so any exception thrown during creation or setup still triggers the finally to delete promptFilePath.
989-993:⚠️ Potential issue | 🟠 MajorCreate the temp prompt file with restrictive permissions.
fs.writeFileSync(promptFilePath, promptText, 'utf8')inherits the process umask, so these files are commonly created as0644on Unix-like hosts. This file contains the full merged prompt and should be written as0600from the start.🔐 Proposed fix
- fs.writeFileSync(promptFilePath, promptText, 'utf8'); + fs.writeFileSync(promptFilePath, promptText, { encoding: 'utf8', mode: 0o600 });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 989 - 993, The temp prompt file is being created with fs.writeFileSync(promptFilePath, promptText, 'utf8') which inherits the process umask and can be world-readable; change the write to explicitly set restrictive permissions by calling fs.writeFileSync with an options object that includes encoding 'utf8' and mode 0o600 (e.g., replace the current fs.writeFileSync call for promptFilePath with one that passes { encoding: 'utf8', mode: 0o600 }); locate the code around getAutomakerDir, promptId and promptFilePath to update that write and ensure the file is created with 0600 from the start.
540-566:⚠️ Potential issue | 🟠 MajorFail fast for native Windows long prompts.
cmd/directinstalls still fall through tobash -c ... "$(cat ...)". That assumes Bash is present on Windows, and even if it is, the expanded prompt still has to fit in the finalcursor-agent.execommand line, so this path does not reliably solve the Win32 limit. Please branch native Windows here and return a clear “use WSL / shorten the prompt” error instead of the Bash fallback.🐛 Suggested direction
+ if ( + process.platform === 'win32' && + (this.detectedStrategy === 'cmd' || this.detectedStrategy === 'direct') + ) { + throw new Error( + 'Long prompts require Cursor via WSL because native Windows installs cannot bypass the Win32 command-line limit.' + ); + } + return { command: 'bash', args: ['-c', fullCommand], cwd,
🧹 Nitpick comments (1)
apps/server/src/providers/cursor-provider.ts (1)
432-567: Add focused coverage for the long-prompt branches.The current unit tests only exercise
--resume, so they will not catch regressions inomitPrompt, temp-file spawning, or the WSL/npx/native-Windows branching introduced here. A small table-driven test aroundbuildCliArgs(..., { omitPrompt: true })andbuildSubprocessOptionsWithPromptFile()would lock this behavior down.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/providers/cursor-provider.ts` around lines 432 - 567, Add unit tests that exercise buildCliArgs(..., { omitPrompt: true }) and buildSubprocessOptionsWithPromptFile(...) in a table-driven way: include cases for cliPath containing 'cursor' vs 'cursor-agent' (to verify 'agent' subcommand), readOnly true/false (to assert '--mode ask --trust' vs '--force'), model !== 'auto', sdkSessionId present, and extras.omitPrompt true to ensure the prompt is omitted; separately test buildSubprocessOptionsWithPromptFile with useWsl + wslCliPath (assert command is 'wsl.exe' and args include the WSL path and bash -c with "$(cat ...)"), with detectedStrategy === 'npx' (assert command 'bash' and args '-c' run full npx command with escaped npxArgs), and the native case (command 'bash' with fullCommand), and in all cases assert that paths are escaped via escapeForBashSingleQuoted and env/timeout/abortController/cwd are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 986-1007: The temp prompt file creation and all pre-spawn setup
must be inside the same try/finally so the prompt file is always cleaned up;
move the start of the try block to begin before the temp-file creation (the
block that uses useTempFile, getAutomakerDir, randomBytes, promptFilePath,
fs.writeFileSync) and keep the existing finally that removes promptFilePath,
ensuring buildCliArgs, buildSubprocessOptionsWithPromptFile (and the else-path
calling buildCliArgs/buildSubprocessOptions) are executed inside that try;
update the scope so any exception thrown during creation or setup still triggers
the finally to delete promptFilePath.
- Around line 989-993: The temp prompt file is being created with
fs.writeFileSync(promptFilePath, promptText, 'utf8') which inherits the process
umask and can be world-readable; change the write to explicitly set restrictive
permissions by calling fs.writeFileSync with an options object that includes
encoding 'utf8' and mode 0o600 (e.g., replace the current fs.writeFileSync call
for promptFilePath with one that passes { encoding: 'utf8', mode: 0o600 });
locate the code around getAutomakerDir, promptId and promptFilePath to update
that write and ensure the file is created with 0600 from the start.
---
Nitpick comments:
In `@apps/server/src/providers/cursor-provider.ts`:
- Around line 432-567: Add unit tests that exercise buildCliArgs(..., {
omitPrompt: true }) and buildSubprocessOptionsWithPromptFile(...) in a
table-driven way: include cases for cliPath containing 'cursor' vs
'cursor-agent' (to verify 'agent' subcommand), readOnly true/false (to assert
'--mode ask --trust' vs '--force'), model !== 'auto', sdkSessionId present, and
extras.omitPrompt true to ensure the prompt is omitted; separately test
buildSubprocessOptionsWithPromptFile with useWsl + wslCliPath (assert command is
'wsl.exe' and args include the WSL path and bash -c with "$(cat ...)"), with
detectedStrategy === 'npx' (assert command 'bash' and args '-c' run full npx
command with escaped npxArgs), and the native case (command 'bash' with
fullCommand), and in all cases assert that paths are escaped via
escapeForBashSingleQuoted and env/timeout/abortController/cwd are preserved.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7a846cd3-afee-4c7c-a267-4812d4ae7fa7
📒 Files selected for processing (2)
.gitattributesapps/server/src/providers/cursor-provider.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- .gitattributes
Fix Cursor agent "your message was just a dash"
Problem
When executing a feature with the Cursor model, the agent responded "your message was just a dash" instead of implementing the task, even when the feature description was a full Markdown spec (e.g. Android project scaffold).
Root Cause
cursor-agent does not support reading prompts from stdin. Unlike codex (which uses
-to mean "read from stdin"), cursor-agent treats the prompt as a positional argument. Passing-as the last argument caused the agent to receive the literal string"-"as the prompt.Solution
1. Pass prompt as argument (
cursor-provider.ts)-.cursor-agent.buildCliArgs()to append the prompt instead of-.2. Temp-file fallback for long prompts (
cursor-provider.ts).automaker/.cursor-prompt-{id}) and passed viabash -c 'cursor-agent ... "$(cat /path)"'to avoid platform command-line limits (~32KB on Windows).finallyblock.3. Change
---separator (cli-provider.ts)---system/user prompt separator inembedSystemPromptIntoPrompt()with=== IMPLEMENTATION TASK ===to avoid markdown/YAML parsing issues.Files Changed
apps/server/src/providers/cursor-provider.ts— prompt passing, temp-file fallback, cleanup.apps/server/src/providers/cli-provider.ts— new prompt separator.Summary by CodeRabbit
New Features
Improvements
Chores